home *** CD-ROM | disk | FTP | other *** search
/ Scene 96 / Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso / misc / coding / vgacodng / part08.txt < prev    next >
Text File  |  1996-12-15  |  13KB  |  351 lines

  1.  
  2.                              VGA-Kurs - Part #8
  3.  
  4. Hallo! Leider mußte 'T.C.P.'s Beginner's Guide To VGA Coding' in der letzten
  5. Ausgabe ausfallen, weil ich keine Zeit hatte, aber diesmal gibt es wieder einen
  6. neuen Teil, und zwar den 8.!
  7. Und der behandelt auf vielfachen Wunsch (von 2 Lesern) das Thema 'Plasmas'.
  8. Erstmal eine kleine Erklärung für die, die noch nie solch einem Effekt begegnet
  9. sind: Es gibt zwei Arten von Plasmas. Das Colorcycle- und das Realtime- oder
  10. auch Sinus-Plasma. Beim Colorcycle-Plasma handelt es sich um ein nach einer
  11. bestimmten Formel vorberechnetes, buntes Bild, dessen Farben einfach per
  12. Palettenrotation vertauscht werden. Um das Bild zu berechnen bedient man sich
  13. der Iteration. Es werden also 9 Punkte (8 am Rand, einer in der Mitte)
  14. zufälliger Farbe auf den Screen gesetzt, so daß sie ihn in 4 Rechtecke teilen.
  15. Nun verfährt man mit diesen Rechtecken genauso wie mit dem Screen am Anfang,
  16. nur daß die neuen Punkte aus den alten durch Interpolation errechnet werden.
  17. Da diese Art von Plasma aber sowieso die uninteressantere ist, hier nur
  18. schnell der Source und dann ohne große Erklärungen zum Sinus-Plasma.
  19.  
  20. uses crt;
  21.  
  22. var n       : byte;
  23.     Palette : array[0..767] of byte;
  24.  
  25. function newcol(mc,n,dvd:integer) : byte;
  26. begin
  27.   newcol := ((mc+n-random(n)) div dvd) mod 192;
  28. end;
  29.  
  30. procedure subdivide(x1,y1,x2,y2:word);
  31. var x,y,dxy,n1,n2,n3,n4 : word;
  32.  
  33. begin
  34.   if (x2-x1 < 2) and (y2-y1 < 2) then exit;
  35.   x := (x2+x1) div 2;
  36.   y := (y2+y1) div 2;
  37.   n1 := mem[$A000:320*y1+x1];
  38.   n2 := mem[$A000:320*y2+x1];
  39.   n3 := mem[$A000:320*y1+x2];
  40.   n4 := mem[$A000:320*y2+x2];
  41.   dxy := 5 * (x2-x1+y2-y1) div 3;
  42.   if mem[$A000:320*y1+x] = 0 then mem[$A000:320*y1+x] := newcol(n1+n3,dxy,2);
  43.   if mem[$A000:320*y+x1] = 0 then mem[$A000:320*y+x1] := newcol(n1+n2,dxy,2);
  44.   if mem[$A000:320*y+x2] = 0 then mem[$A000:320*y+x2] := newcol(n3+n4,dxy,2);
  45.   if mem[$A000:320*y2+x] = 0 then mem[$A000:320*y2+x] := newcol(n2+n4,dxy,2);
  46.   mem[$A000:320*y+x] := newcol(n1+n2+n3+n4,dxy,4);
  47.   subdivide(x1,y1,x,y);
  48.   subdivide(x,y1,x2,y);
  49.   subdivide(x1,y,x,y2);
  50.   subdivide(x,y,x2,y2);
  51. end;
  52.  
  53. procedure SetPal(col,r,g,b:byte);
  54. begin
  55.   port[$3C8] := col;
  56.   port[$3C9] := r;
  57.   port[$3C9] := g;
  58.   port[$3C9] := b;
  59. end;
  60.  
  61. procedure RotatePalette;
  62. var Temp : array[0..2] of byte;
  63.  
  64. begin
  65.   repeat
  66.     move(Palette,Temp,3);
  67.     move(Palette[3],Palette[0],765);
  68.     move(Temp,Palette[765],3);
  69.     for n := 0 to 255 do setpal(n,Palette[n*3],Palette[n*3+1],Palette[n*3+2]);
  70.   until keypressed;
  71. end;
  72.  
  73. begin
  74.   randomize;
  75.   asm mov ax,13h; int 10h end;  { In Mode 13h schalten }
  76.   fillchar(Palette,768,0);      { Palette erstellen }
  77.   for n := 0 to 255 do begin
  78.     Palette[n*3+1] := n div 2;
  79.     Palette[n*3+2] := n;
  80.     setpal(n,0,n div 2,n);
  81.   end;
  82.   subdivide(0,0,319,199);       { Plasma aufbauen }
  83.   RotatePalette;                { Palette rotieren }
  84.   readkey;
  85.   asm mov ax,3; int 10h end;    { Zurück zum Textmodus }
  86. end.
  87.  
  88. Der Grund, warum dieses Plasma eher langweilig wirkt, ist das Fehlen einer
  89. Bewegung. Außerdem wären etwas rundere Formen angenehm.
  90. Diese Mängel behebt das Sinus-Plasma. Sein Nachteil ist allerdings, daß es
  91. sehr viel mehr Rechenzeit erfordert, deshalb sollte es möglichst in Assembler
  92. geschrieben werden.
  93. Doch wie macht man das überhaupt? Zuerst benötigt man eine (Co)Sinus-Tabelle.
  94. Diese entscheidet nachher die Form des Plasmas, allerdings nicht allein.
  95. Auch wie die Werte aus dieser Tabelle entnommen werden, ist relevant.
  96. Als Beispiel benutzen wir hier die Formel
  97.  
  98. Farbwert := SinTab[Wert1] + SinTab[Wert2] + SinTab[Wert3] + SinTab[Wert4]
  99.  
  100. Die vier Indizes sind Werte, die bei jedem Durchlauf um einen bestimmten oder
  101. zufälligen Wert erhöht werden, wodurch eine Bewegung zustande kommt.
  102. Da wir die Variablen innerhalb der Loops verändern, müssen wir sie vorher in
  103. Hilfsvariablen (TWert1-4) sichern.
  104. Die Variablen Wert1 und Wert2 sind für das horizontale und Wert3 und Wert4 für
  105. das vertikale Muster des Plasmas verantwortlich. Sie werden in den
  106. entsprechenden Loops um feste Werte erhöht, die die Größe der Plasma-Kreise
  107. bestimmen.
  108. Das Ganze nun erstmal in einer 100%-Pascal-Version:
  109.  
  110. uses crt;
  111.  
  112. var Wert1,Wert2,Wert3,Wert4 : byte;
  113.     TWert1,TWert2,TWert3,TWert4 : byte;
  114.     SinTab : array[0..255] of byte;
  115.     n1,n2 : word;
  116.     col : byte;
  117.  
  118. procedure SetPal(col,r,g,b:byte);
  119. begin
  120.   port[$3C8] := col;
  121.   port[$3C9] := r;
  122.   port[$3C9] := g;
  123.   port[$3C9] := b;
  124. end;
  125.  
  126. procedure CalcSinus(Ofs,Amp:byte;Len,Par:word);
  127. begin
  128.   for n1 := 0 to Len do SinTab[n1] := round(sin(n1/Par*pi*Len/180*2)*Amp)+Ofs;
  129. end;
  130.  
  131. begin
  132.   CalcSinus(32,31,255,360);
  133.   asm mov ax,13h; int 10h end;
  134.   for n1 := 0 to 127 do begin
  135.     setpal(n1,n1 div 6,n1 div 3,n1 div 3);
  136.     setpal(255-n1,n1 div 6,n1 div 3,n1 div 3);
  137.   end;
  138.   repeat
  139.     TWert3 := Wert3;
  140.     TWert4 := Wert4;
  141.     for n1 := 0 to 319 do begin
  142.       TWert1 := Wert1;
  143.       TWert2 := Wert2;
  144.       for n2 := 0 to 199 do begin
  145.         col := SinTab[TWert1] + SinTab[TWert2] +
  146.                SinTab[TWert3] + SinTab[TWert4];  { Farbwert berechnen }
  147.         mem[$A000:n2*320+n1] := col;
  148.         inc(TWert1,4);
  149.         inc(TWert2,3);
  150.       end;
  151.       inc(TWert3,4);
  152.       inc(TWert4,5);
  153.     end;
  154.     dec(Wert1,4);            { Bewegung des Plasmas }
  155.     inc(Wert3,4);
  156.     inc(Wert1,random(1));    { Zufallswerte bewirken, daß das Plasma }
  157.     dec(Wert2,random(2));    { etwas unregelmäßig wird }
  158.     inc(Wert3,random(1));
  159.     dec(Wert4,random(2));
  160.   until keypressed;
  161.   readkey;
  162.   asm mov ax,3; int 10h end;
  163. end.
  164.  
  165. Wer nicht gerade einen schnellen 486 hat, wird wohl mit der Geschwindigkeit
  166. nicht ganz zufrieden sein, deshalb jetzt nochmal die Repeat-Schleife in der
  167. Assembler-Version:
  168.  
  169.   { Einfach die Repeat-Schleife aus dem oberen Listing löschen und diese
  170.     Zeilen einfügen: }
  171.   asm
  172.     mov     ax,0A000h                  { VGA-Segment nach ES }
  173.     mov     es,ax
  174.   @mainloop:
  175.     xor     di,di                      { Bildschirmoffset auf 0 }
  176.     mov     al,Wert3                   { Werte sichern }
  177.     mov     TWert3,al
  178.     mov     al,Wert4
  179.     mov     TWert4,al
  180.     mov     n1,0                       { Zähler initialisieren }
  181.   @loop1:
  182.     mov     al,Wert1                   { Werte sichern }
  183.     mov     TWert1,al
  184.     mov     al,Wert2
  185.     mov     TWert2,al
  186.     mov     n2,0                       { Zähler initialisieren }
  187.   @loop2:
  188.     xor     bx,bx                      { Farbwert wird in BX berechnet }
  189.     mov     al,TWert4                  { Index holen }
  190.     xor     ah,ah
  191.     mov     si,ax
  192.     mov     al,[si+offset SinTab]      { Wert auslesen }
  193.     add     bx,ax                      { und auf BX addieren }
  194.     mov     al,TWert3
  195.     mov     si,ax
  196.     mov     al,[si+offset SinTab]
  197.     add     bx,ax
  198.     mov     al,TWert2
  199.     mov     si,ax
  200.     mov     al,[si+offset SinTab]
  201.     add     bx,ax
  202.     mov     al,TWert1
  203.     mov     si,ax
  204.     mov     al,[si+offset SinTab]
  205.     add     bx,ax
  206.     mov     es:[di],bl                 { Pixel setzen }
  207.     inc     di                         { Bildschirmoffset erhöhen }
  208.     add     TWert1,4                   { Indizes erhöhen }
  209.     add     TWert2,3
  210.     inc     n2
  211.     cmp     n2,320
  212.     jne     @loop2
  213.     add     TWert3,4
  214.     add     TWert4,5
  215.     inc     n1
  216.     cmp     n1,200
  217.     jne     @loop1
  218.     sub     Wert1,4
  219.     add     Wert3,4
  220.     mov     ah,0Bh
  221.     int     21h
  222.     or      al,al
  223.     jz      @mainloop
  224.   end;
  225.  
  226. Man kann nun das Aussehen des Plasmas verändern, indem man z.B. das Offset und
  227. die Amplitude der Sinustabelle beim Aufruf von CalcSinus ändert.
  228. Außerdem könnte man hier parallel noch eine Palettenrotation einbauen, das
  229. kostet nicht viel Rechenzeit, sieht aber um so besser aus.
  230. Dieses Beispiel ist natürlich nur eine von vielen Methoden, Sinus-Plasmas zu
  231. coden. Es wurden schon einige hier im PCH abgedruckt, wenn ihr sie noch habt,
  232. seht sie euch doch nochmal an, ihr erfahrt dort bestimmt noch einiges mehr.
  233. (Hey, an dieser Stelle hat der VGA-Kurs das 100.000ste Byte erreicht!!!)
  234.  
  235. Dieser Teil des Kurses ist noch nicht zu Ende! Es folgt nun ein Bonusteil,
  236. und zwar über Fraktale, wie Onkel Joe es sich im PCH 3/96 gewünscht hat.
  237. (Ich hätte nichts gegen weitere Bonusteile, wenn euch also ein Randthema der
  238. VGA-Programmierung interessiert, schreibt mir, und ich werde mal sehen, ob ich
  239. dazu was schreiben kann.)
  240. Jeder kennt sie, jeder ist fasziniert von ihnen: Fraktale. Doch wie werden
  241. diese Dinger eigentlich berechnet?
  242. Das funktioniert so ähnlich wie bei der Berechnung der Colorcycle-Plasmas,
  243. allerdings kommt man hier nicht um Real-Zahlen herum, was sich nicht unerheblich
  244. auf die Berechnungsgeschwindigkeit auswirkt.
  245. Wie beim Plasma (das übrigens auch ein Fraktal ist) wird mit Iteration
  246. gearbeitet, d.h. daß die Werte, die im letzen Schleifendurchlauf berechnet
  247. wurden, als Basis für die neuen Berechnungen herangezogen werden.
  248. Der folgende Source behandelt die beiden populärsten Fraktal-Typen, Mandelbrot
  249. und Julia. Für die beiden Typen ist jeweils eine Prozedur zuständig, die aus
  250. den X- und Y-Koordinaten des Pixels den zugehörigen Farbwert des Fraktals
  251. berechnet. Ihr könnt die Berechnungsweise aus den Prozeduren entnehmen, die
  252. Erklärung dieser Formeln liegt leider außerhalb meines mathematischen
  253. Wissensstandes.
  254.  
  255. {$N+,E+}
  256. uses crt;
  257. { Diese Konstanten können verändert werden, um das Aussehen der Fraktale zu
  258.   bestimmen. }
  259. const Colors = 32;         { Anzahl der Farben des Fraktals }
  260.       Width = 320;         { Breite des Fraktals }
  261.       Height = 200;        { Höhe des Fraktals }
  262.       Limit = 8.0;         { Berechnungsgrenze, regelt Schärfe }
  263.       XRMin = -2.0;        { Linke Grenze des Fraktals }
  264.       XRMax = 1.0;         { Rechte Grenze des Fraktals }
  265.       YRMin = -1.3;        { Obere Grenze des Fraktals }
  266.       YRMax = 1.3;         { Untere Grenze des Fraktals }
  267.  
  268. type real = double;        { Zur Optimierung der Fließkomma-Berechnungen }
  269.  
  270. var XPos,YPos : word;
  271.     RealP,ImagP : real;
  272.     CurrX,CurrY : real;
  273.     a2,b2 : real;
  274.     n : byte;
  275.  
  276. function CalcColorMandel(XPos,YPos:word) : byte;
  277. begin
  278.   CurrX := XPos / Width * (XRMax-XRMin) + XRMin;
  279.   CurrY := YPos / Height * (YRMax-YRMin) + YRMin;
  280.   RealP := 0;
  281.   ImagP := 0;
  282.   n := 0;
  283.   repeat
  284.     a2 := sqr(RealP);
  285.     b2 := sqr(ImagP);
  286.     ImagP := 2 * RealP * ImagP + CurrY;
  287.     RealP := a2 - b2 + CurrX;
  288.     inc(n);
  289.   until (n >= Colors) or (a2+b2 >= Limit);
  290.   CalcColorMandel := n - 1;
  291. end;
  292.  
  293. function CalcColorJulia(XPos,YPos:word) : byte;
  294. begin
  295.   CurrX := 0.07;
  296.   CurrY := -0.4;
  297.   RealP := -2 + 0.5 + XPos / (Width/3);
  298.   ImagP := 2 - 0.5 - YPos / (Height/3);
  299.   n := 0;
  300.   repeat
  301.     a2 := sqr(ImagP) - sqr(RealP) + CurrX;
  302.     b2 := 2 * RealP * ImagP + CurrY;
  303.     RealP := a2;
  304.     ImagP := b2;
  305.     inc(n);
  306.   until (n >= Colors) or (sqr(a2)+sqr(b2) > 4) or
  307.         (abs(a2) > 2) or (abs(b2) > 2);
  308.   CalcColorJulia := n - 1;
  309. end;
  310.  
  311. begin
  312.   asm mov ax,13h; int 10h end;   { In Mode 13h schalten }
  313.   for YPos := 0 to Height-1 do   { Mandelbrot-Menge aufbauen }
  314.     for XPos := 0 to Width-1 do
  315.       mem[$A000:YPos*320+XPos] := CalcColorMandel(XPos,YPos);
  316.   readkey;
  317.   for YPos := 0 to Height-1 do   { Julia-Menge aufbauen }
  318.     for XPos := 0 to Width-1 do
  319.       mem[$A000:YPos*320+XPos] := CalcColorJulia(XPos,YPos);
  320.   readkey;
  321.   asm mov ax,3; int 10h end;     { Zurück zum Textmodus }
  322. end.
  323.  
  324. So, wir sind mal wieder am Ende angelangt, und es folgt die Vorschau auf den
  325. nächsten Teil: Ich will nicht zu viel versprechen, aber ich werde beim nächsten
  326. mal mit 3D-Grafik beginnen. Dieses Thema ist wirklich nicht leicht anschaulich
  327. zu machen, vor allem Leser ohne Mathekenntnisse (Geometrie!) werden einige
  328. Probleme haben, aber ich werde es trotzdem versuchen. Also, bis bald!
  329.  
  330.  
  331.  
  332.  
  333.  
  334. [ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
  335. [                                                                      ]
  336. [ No  part   of  this   document  may  be   reproduced,   transmitted, ]
  337. [ transcribed,  stored in a  retrieval system,  or translated into any ]
  338. [ human or computer language, in any form or by any means; electronic, ]
  339. [ mechanical,  magnetic,  optical,   chemical,  manual  or  otherwise, ]
  340. [ without the expressed written permission of the author.              ]
  341. [                                                                      ]
  342. [ The information  contained in this text  is believed  to be correct. ]
  343. [ The text is subject to change  without notice and does not represent ]
  344. [ a commitment on the part of the author.                              ]
  345. [ The author does not make a  warranty of any kind with regard to this ]
  346. [ material, including,  but not limited to,  the implied warranties of ]
  347. [ merchantability  and fitness  for a particular  purpose.  The author ]
  348. [ shall not be liable for errors contained herein or for incidental or ]
  349. [ consequential damages in connection with the furnishing, performance ]
  350. [ or use of this material.                                             ]
  351.